package fag.core.game;

import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.util.ArrayList;
import java.util.List;

import fag.core.collision.Gjk;
import fag.core.collision.Hitbox;
import fag.core.geometry.Rectangle2D;
import fag.core.geometry.Vector2D;
import fag.core.model.Animation;
import fag.core.model.Model;
import fag.core.model.Sprite;

/**
 * Classe des objets de scène.
 * @author lumz
 */
public class Doodad {

	Scene scene = null;
	
	private AffineTransform transformation = new AffineTransform();
	
	private Model model = null;
	// TODO : Remarque : Modifier quelque chose en rapport avec l'animation devrait peut être se faire grâce à Doodad.getAnimation().etc..
	private Animation animation = null;
	
	private boolean isBoundsCached = false;
	private boolean isHitboxCached = false;
	
	private Rectangle2D boundsCache = null;
	private Hitbox hitboxCache = null;
	
	/**
	 * Constructeur par défaut.
	 * @param model Le modèle à utiliser par ce Doodad.
	 */
	public Doodad(Model model) {
		this.model = model;
		animation = this.model.getDefaultAnimation(); 
	}
	
	/**
	 * Obtenir la vitesse de l'animation.
	 * @return Le facteur de vitesse de l'animation.
	 */
	public double getAnimationSpeed() {
		
		return animation.getSpeed();
	}
	
	/**
	 * Accesseur de la boîte englobante.
	 * @return La boîte englobante de ce Doodad.
	 */
	// Remarque performances : Recalculer la boîte englobante du polygone de transformation.transform(...).
	// implique un coût qui pourrait être moindre s'il s'agissait d'un cercle englobant.
	public Rectangle2D getBounds() {
		
		// Cache hit
		if(isBoundsCached)
			return boundsCache.clone();
		
		// Création du cache
		boundsCache = animation.getCurrentBounds().transform(transformation).getBounds();
		
		isBoundsCached = true;
		
		return boundsCache.clone();
	}
	
	/**
	 * Accesseur de la liste des boîtes de collisions.
	 * @return La liste des boîtes de collisions.
	 */
	public Hitbox getHitbox() {
		
		// Cache hitbox
		if(isHitboxCached) {
		
			return hitboxCache.clone();
		}			
		
		// Création du cache
		hitboxCache = animation.getCurrentHitbox().transform(transformation);
		isHitboxCached = true;
		
		return hitboxCache.clone();
	}
	
	/**
	 * Accesseur de la position du Doodad.
	 * @return La position du Doodad.
	 */
	public Vector2D getLocation() {
		return new Vector2D(transformation.getTranslateX(), transformation.getTranslateY());
	}

	/**
	 * Obtenir la coordonnée x du Doodad.
	 * @return La coordonnée x du Doodad.
	 */
	public double getX() {
		return transformation.getTranslateX();
	}
	
	/**
	 * Obtenir la coordonnée y du Doodad.
	 * @return La coordonnée y du Doodad.
	 */
	public double getY() {
		return transformation.getTranslateY();
	}

	/**
	 * Obtenir la propriété de bouclage de l'animation.
	 * @return Vrai si l'animation boucle, faux sinon.
	 */
	public boolean isLooping() {
		return animation.isLooping();
	}
	
	/**
	 * Fixer l'animation courante.
	 * @param name Le nom de l'animation disponible dans le modèle associé.
	 */
	public void setAnimation(String name) {
		animation = model.getAnimation(name);
	}
	
	/**
	 * Fixer la vitesse de l'animation.
	 * @param speed Le facteur de vitesse de l'animation.
	 */
	public void setAnimationSpeed(double speed) {
		animation.setSpeed(speed);
	}
	
	/**
	 * Definir la propriété de bouclage de l'animation.
	 * @param isLooping Vrai si on veut boucler , faux sinon.
	 */
	public void setLooping(boolean isLooping) {
		animation.setLoop(isLooping);
	}
	
	/**
	 * Placer ce Doodad dans la scène.
	 * @param x La composante x.
	 * @param y La composante y.
	 */
	public void setLocation(double x, double y) {
		
		// TODO : Restreindre à la scène
		
		isBoundsCached = false;
		isHitboxCached = false;
		
		Rectangle2D bounds = animation.getCurrentSprite().getBounds();
		
		if(scene != null) {
			scene.addDirtyRegion(bounds.transform(this.transformation).getBounds());
		}
		
		this.transformation.setToTranslation(x, y);
		
		if(scene != null)
			scene.addDirtyRegion(bounds.transform(this.transformation).getBounds());
	}
	
	/**
	 * Placer ce Doodad dans la scène.
	 * @param location La nouvelle position.
	 */
	public void setLocation(Vector2D location) {
		setLocation(location.x, location.y);
	}
	
	/**
	 * Dessine le sprite courant.
	 * @param graphics2d Le contexte de dessin.
	 */
	void draw(Graphics2D graphics2d) {
		
		// Transformation du plan pour peindre l'objet à sa position
		AffineTransform old = graphics2d.getTransform();
		graphics2d.transform(transformation);

		Sprite sprite = animation.getCurrentSprite();
		
		// TODO : Gérer la transparence, regarder au niveau de BufferedImage il semble qu'il y ai quelque chose à modifier
		// pour prendre en compte la transparence.
		
		graphics2d.drawImage(sprite.getBufferedImage(),null,(int)sprite.getOffSet().x,(int)sprite.getOffSet().y);
		
		// Restauration du contexte graphique
		graphics2d.setTransform(old);
	}
	
	/**
	 * Avancer l'animation de l'intervalle dt.
	 * @param dt L'avancement de l'animation.
	 */
	void forward(double dt) {
		
		// TODO : Il n'est pas nécéssaire de redessiner si la frame de l'animation n'a pas changé
		
		Rectangle2D bounds = animation.getCurrentSprite().getBounds();
		
		if(scene != null)
			scene.addDirtyRegion(bounds.transform(transformation).getBounds());
		
		animation.forward(dt);
	}
}
